import { computed, onUnmounted, readonly, ref, Ref, unref } from 'vue';
import { hookResult, IS_MODAL } from './bindResult';

type UnionType<T> = T extends readonly any[] ? T[number] : T;

export interface IModalProps<T extends readonly string[]> {
  /* 在modal中显示loading，自动控制 */
  modalLoading?: boolean;

  onShow?: (mode: UnionType<T>, otherParams?: any) => void | Promise<void>;

  onHide?: (mode: UnionType<T>) => void;

  renderTitle?: (mode: UnionType<T>) => string;

  onOkClick?: (mode: UnionType<T>) => Promise<any> | any;

  onCancelClick?: (mode: UnionType<T>) => Promise<any> | any;

  okText?: string;

  onCustomAction?: (mode: UnionType<T>, action: string) => Promise<any> | any;

  isFullScreenDrawer?: boolean;
}

export interface ModeModalProps<T extends readonly string[]>
  extends IModalProps<T> {
  readonly modes: T;
}

export interface ModeModalResult<T> {
  showModal: (mode: UnionType<T>, otherParams?: any) => void;

  visible: Ref<boolean>;

  isShow: (val: UnionType<T>) => boolean;

  hideModal: () => void;

  bindProps: any;

  triggerOk: () => void;

  triggerCancel: () => void;

  triggerSuccess: (mode: UnionType<T>, otherParams?: any) => void;

  modeIs: (mode: UnionType<T>) => boolean;

  changeMode: (mode: UnionType<T>) => void;

  expose: {
    showModal: (mode: UnionType<T>, otherParams?: any) => void;
  };
}

export const rowLoadingKey = Symbol('rowLoadingKey');

export type FOnSuccess = <T>(mode: UnionType<T>, res: any) => void;

const DefaultTitleMap: Record<string, string> = {
  edit: '编辑',
  add: '新建',
  detail: '查看',
  submit: '提交',
};

const defaultTitleRender = (mode: string) => {
  if (DefaultTitleMap[mode]) {
    return DefaultTitleMap[mode];
  }
  return '未设置';
};

export const createModeModalBinder = <T extends readonly string[]>(
  props: ModeModalProps<T>
) => {
  const {
    modalLoading = false,
    modes,
    onShow,
    renderTitle,
    onOkClick,
    onHide,
    okText = '确认',
    onCancelClick,
    isFullScreenDrawer = false,
  } = props;

  const currentShowMode = ref(modes[0]);

  const dialogVisible = ref<boolean>(false);

  const okLoading = ref(false);

  const cancelLoading = ref(false);

  const openLoading = ref(false);

  const successCbSet = new Set<FOnSuccess>();

  const registerSuccess = (successCb: FOnSuccess) => {
    successCbSet.add(successCb);
  };
  const unregisterSuccess = (successCb: FOnSuccess) => {
    successCbSet.delete(successCb);
  };

  const onSuccess = (mode: UnionType<T>, res: any) => {
    successCbSet.forEach((cb) => {
      cb(mode, res);
    });
  };

  onUnmounted(() => {
    successCbSet.clear();
  });

  const showModal: ModeModalResult<typeof modes>['showModal'] = (
    mode: UnionType<T>,
    otherParams?: any
  ) => {
    // @ts-ignore
    if (modes.includes(mode)) {
      if (onShow) {
        if (modalLoading) {
          openLoading.value = true;
          currentShowMode.value = mode;
          dialogVisible.value = true;
        } else if (otherParams) {
          otherParams[rowLoadingKey] = true;
        }

        return Promise.resolve(onShow(unref(mode), otherParams))
          .then(() => {
            if (!modalLoading) {
              // @ts-ignore
              currentShowMode.value = mode;
              dialogVisible.value = true;
            }
          })
          .finally(() => {
            if (modalLoading) {
              openLoading.value = false;
            } else if (otherParams) {
              otherParams[rowLoadingKey] = false;
            }
          });
      }
      // @ts-ignore
      currentShowMode.value = mode;
      dialogVisible.value = true;
      return Promise.resolve();
    }
    return Promise.reject(new Error(`不存在mode:${mode}`));
  };

  const hideModal = () => {
    if (onHide) {
      onHide(currentShowMode.value as UnionType<T>);
    }
    dialogVisible.value = false;
  };

  const modalTitle = computed(() => {
    return renderTitle
      ? // @ts-ignore
        renderTitle(currentShowMode.value)
      : defaultTitleRender(currentShowMode.value);
  });

  const onOk = () => {
    if (onOkClick) {
      okLoading.value = true;
      // @ts-ignore
      Promise.resolve(onOkClick(currentShowMode.value))
        .then((res) => {
          if (onSuccess) {
            onSuccess(currentShowMode.value as UnionType<T>, res);
          }
          hideModal();
        })
        .finally(() => {
          okLoading.value = false;
        });
    } else {
      hideModal();
    }
  };

  const onCancel = () => {
    if (onCancelClick) {
      cancelLoading.value = true;
      // @ts-ignore
      Promise.resolve(onCancelClick(currentShowMode.value))
        .then((res) => {
          if (onSuccess) {
            onSuccess(currentShowMode.value as UnionType<T>, res);
          }
          hideModal();
        })
        .finally(() => {
          cancelLoading.value = false;
        });
    } else {
      hideModal();
    }
  };

  const closable = computed(() => {
    return !okLoading.value;
  });

  function isShow(val: UnionType<T>) {
    return currentShowMode.value === val;
  }

  const drawerProps = isFullScreenDrawer
    ? {
        placement: 'bottom',
        popupContainer: '#page-container',
        height: 'calc(100% - 2rem)',
        closable: true,
        mask: true,
        class: 'page-drawer shadow-lg shadow-blue-200',
      }
    : {};

  const modeIs = (mode: UnionType<T>) => {
    return mode === currentShowMode.value;
  };

  const changeMode = (mode: UnionType<T>) => {
    currentShowMode.value = mode;
  };

  return [
    {
      visible: dialogVisible,
      isShow,
      showModal,
      hideModal,
      modeIs,
      changeMode,
      triggerOk: onOk,
      triggerCancel: onCancel,
      triggerSuccess: onSuccess,
      expose: {
        showModal,
        registerSuccess,
        unregisterSuccess,
        [IS_MODAL]: true,
      },
    },
    {
      mode: readonly(currentShowMode),
      visible: dialogVisible,
      title: modalTitle,
      closable,
      okText,
      loading: openLoading,
      okLoading,
      cancelLoading,
      onOk,
      onCancel,
      ...drawerProps,
    },
  ] as const;
};

export function useModeModalBinder<T extends readonly string[]>(
  props: ModeModalProps<T>
): ModeModalResult<T> {
  const [obj, bindProps] = createModeModalBinder(props);
  return hookResult(obj, bindProps);
}
